

export class Queue<T> {

    private _elements: (T | undefined)[];
    private _headIndex;

    public constructor(elements?: readonly T[]) {
        this._elements = elements ? elements.slice() : [];
        this._headIndex = 0;
    }

    public get size() {
        return this._elements.length;
    }

    public isEmpty() {
        return this._headIndex === this._elements.length;
    }

    public enqueue(...items: T[]) {
        this._elements.push(...items);
    }

    public dequeue(): T {
        if (this.isEmpty()) {
            throw new Error("Queue is empty.");
        }

        const result = this._elements[this._headIndex] as T;
        this._elements[this._headIndex] = undefined;
        this._headIndex++;

        if (this._headIndex > 100 && this._headIndex > (this._elements.length >> 1)) {
            const newLength = this._elements.length - this._headIndex;
            this._elements.copyWithin(0, this._headIndex);
            this._elements.length = newLength;
            this._headIndex = 0;
        }

        return result;
    }

    public clear(): void {
        this._elements = [];
        this._headIndex = 0;
    }

}
